package top.z.orm.handle.distribution.ds;

import top.z.orm.core.distribution.ds.Route;
import top.z.orm.core.osql.SuidType;
import top.z.orm.core.osql.exception.NoConfigException;
import top.z.orm.handle.osql.core.HoneyConfig;
import top.z.orm.handle.osql.core.HoneyContext;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

/**
 * 一个写数据库, 多个读数据库
 */
public class RwDs implements Route {

    private String       writeDd;
    private List<String> readDsList;

    private static       int    count     = 0;
    private static final int    max_count = Integer.MAX_VALUE - 1000000;
    private static final Random r         = new Random();
    private final        byte[] lock      = new byte[0];

    private static int r_routeWay;

    {
        // 非 static, 每次 new 都会执行. 若 writer 有更新, 这样可以刷新
        init();
    }

    private void init() {
        String wDB = HoneyConfig.getHoneyConfig().multiDs_writeDB;
        String rDB = HoneyConfig.getHoneyConfig().multiDs_readDB;
        // 要判断从配置文件拿来的信息不能为空
        if ((wDB == null || "".equals(wDB.trim())) || (rDB == null || "".equals(rDB.trim()))) {
            throw new NoConfigException("Error: bee.dosql.multi-DS.writeDB and bee.dosql.multi-DS.readDB can not null or empty when bee.dosql.multi-DS.type=1! ");
        }
        setWriteDs(wDB);
        setReadDsList(parseRDb(rDB));
        // 写库不能放在只读库列表
        getReadDsList().remove(wDB);
        r_routeWay = HoneyConfig.getHoneyConfig().rDbRouteWay;
    }

    private List<String> parseRDb(String rDB_str) {
        String[]     s     = rDB_str.split(",");
        List<String> rList = new ArrayList<>();
        Collections.addAll(rList, s);
        return rList;
    }

    @Override
    public String getDsName() {
        RouteStruct routeStruct = HoneyContext.getCurrentRoute();
        if (routeStruct != null && SuidType.SELECT == routeStruct.getSuidType()) {
            return getReadDs(r_routeWay);
        } else {
            return getWriteDs();
        }
    }

    public String getWriteDs() {
        return writeDd;
    }

    public void setWriteDs(String writeDs) {
        //TODO if master change, need update
        this.writeDd = writeDs;
    }

    public String getReadDs() {
        //rand
        return getReadDs(0);
    }

    public String getReadDs(int type) {
        int index;
        if (type == 1) {
            index = poll();
        } else {
            index = rand();
        }
        return getReadDsList().get(index);
    }

    private int poll() {
        int size = readDsList.size();
        if (count > max_count) {
            synchronized (lock) {
                if (count > max_count) count = 0;
            }
        }
        int index = count % size;
        count++;
        return index;
    }

    private int rand() {
        int size = readDsList.size();
        return r.nextInt(size);
    }

    public List<String> getReadDsList() {
        return readDsList;
    }

    public void setReadDsList(List<String> readDsList) {
        this.readDsList = readDsList;
    }
}